/**
 * Copyright (c) 2018-2022, NXOS Development Team
 * SPDX-License-Identifier: Apache-2.0
 * 
 * Contains: system perf
 * 
 * Change Logs:
 * Date           Author            Notes
 * 2023-09-17     JasonHu           Init
 */

#include <base/perf.h>
#include <base/smp.h>
#include <base/log.h>
#include <base/irq.h>
#include <base/syscall.h>
#include <base/delay_irq.h>

NX_CpuUsageInfo CpuUsageInfo[NX_MULTI_CORES_NR];

#define ThisCpuUsageInfo CpuUsageInfo[NX_SMP_GetIdx()]

NX_PRIVATE void UpdateSystemTimes(NX_Thread *thread, NX_PerfTime perfTime, NX_IArch irqDepth, NX_IArch delayIrqDepth)
{
    /* 1. check hard irq */
    if (irqDepth > 1)
    {
        ThisCpuUsageInfo.irq += perfTime;
    }
    /* 2. check delay irq */
    else if (delayIrqDepth > 0) 
    {
        ThisCpuUsageInfo.delayirq += perfTime;
    }
    /* 3. check system time */
    else
    {
        NX_Process *process;
        /* a. update thread */
        thread->resource.usageInfo.system += perfTime;
        /* b. update process */
        process = thread->resource.process;
        if (process != NX_NULL)
        {
            process->usageInfo.system += perfTime;
        }
        /* c. update cpu */
        ThisCpuUsageInfo.system += perfTime;
    }
}

NX_PRIVATE void UpdateUserTimes(NX_Thread *thread, NX_PerfTime perfTime)
{
    NX_Process *process;
    /* a. update thread */
    thread->resource.usageInfo.user += perfTime;
    /* b. update process */
    process = thread->resource.process;
    if (process != NX_NULL)
    {
        process->usageInfo.user += perfTime;
    }
    /* c. update cpu */
    ThisCpuUsageInfo.user += perfTime;
}

NX_PRIVATE void UpdateIdleTimes(NX_Thread *thread, NX_PerfTime perfTime)
{
    ThisCpuUsageInfo.idle += perfTime;
    NX_SMP_GetIdle(NX_SMP_GetIdx())->resource.usageInfo.system += perfTime;
}

void NX_CpuUpdateTimes(void)
{
    NX_Thread *thread;
    NX_PerfTime time = NX_TICKS_PER_MILLISECOND;
    NX_IArch irqDepth, delayIrqDepth;

    thread = NX_ThreadSelf();

    /* user process */
    if (thread->resource.process != NX_NULL)
    {
        if (NX_SyscallGetDepth() > 0)
        {
            /* system */
            irqDepth = NX_IRQ_GetDepth();
            delayIrqDepth = NX_DelayIRQ_GetDepth();
            if (NX_IsIdleThrad(thread) == NX_False || irqDepth > 1 ||  delayIrqDepth > 0)
            {
                UpdateSystemTimes(thread, time, irqDepth, delayIrqDepth);
            }
            else /* idle */
            {
                UpdateIdleTimes(thread, time);
            }
        }
        else
        {
            UpdateUserTimes(thread, time);
        }
    }
    else /* kernel */
    {
        /* system */
        irqDepth = NX_IRQ_GetDepth();
        delayIrqDepth = NX_DelayIRQ_GetDepth();
        if (NX_IsIdleThrad(thread) == NX_False || irqDepth > 1 ||  delayIrqDepth > 0)
        {
            UpdateSystemTimes(thread, time, irqDepth, delayIrqDepth);
        }
        else /* idle */
        {
            UpdateIdleTimes(thread, time);
        }
    }
}

void NX_ThreadUsageInit(NX_UsageInfo *usage)
{
    usage->system = 0;
    usage->user = 0;
}

void NX_PerfInit(void)
{
    int i;

    for (i = 0; i < NX_MULTI_CORES_NR; i++)
    {
        CpuUsageInfo[i].delayirq = 0;
        CpuUsageInfo[i].irq = 0;
        CpuUsageInfo[i].iowait = 0;
        CpuUsageInfo[i].idle = 0;
        CpuUsageInfo[i].system = 0;
        CpuUsageInfo[i].user = 0;
    }
}
